package cn.jasonone.him.util;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.context.ApplicationContext;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.Transport;
import com.corundumstudio.socketio.listener.ConnectListener;
import com.corundumstudio.socketio.listener.DataListener;
import com.corundumstudio.socketio.listener.DisconnectListener;

import cn.jasonone.him.HimConst;
import cn.jasonone.him.HimProperties;
import cn.jasonone.him.event.HimAuthorizationAfterEvent;
import cn.jasonone.him.event.HimAuthorizationBeforeEvent;
import cn.jasonone.him.event.HimEvent;
import cn.jasonone.him.event.HimExceptionEvent;
import cn.jasonone.him.event.HimMessageEvent;
import cn.jasonone.him.model.HimUserInfo;
import cn.jasonone.him.model.Message;
import cn.jasonone.him.service.HimService;
import lombok.extern.slf4j.Slf4j;

/**
 * 服务器工具类
 * 
 * @author Jason
 *
 */
@Slf4j
public class ServerUtil {
	private static SocketIOServer server;
	private static HimService himService;
	private static Map<String, SocketIOClient> clients = Collections.synchronizedMap(new HashMap<>());

	/**
	 * 创建服务器对象
	 * 
	 * @param himProperties 配置对象
	 * @param service       业务对象
	 * @return 服务器对象
	 */
	public static SocketIOServer createServer(HimProperties himProperties, HimService service,
			ApplicationContext application) {
		if (himProperties == null) {
			himProperties = new HimProperties();
		}
		if (service == null) {
			himService = new HimService() {
			};
		} else {
			himService = service;
		}
		Configuration configuration = new Configuration();
		configuration.setPort(himProperties.getPort());
		configuration.setTransports(Transport.POLLING, Transport.WEBSOCKET);
		configuration.setOrigin(himProperties.getOrigin());
		configuration.setHostname(himProperties.getHostname());
		configuration.setAuthorizationListener(new AuthorizationListener() {
			@Override
			public boolean isAuthorized(HandshakeData data) {
				application.publishEvent(
						new HimAuthorizationBeforeEvent(himService, HimConst.AUTHORIZATION_AFTER_EVENT, "权限鉴定", data));
				boolean authorization = false;
				try {
					authorization = himService.isAuthorization(data);
					Map<String, Object> userData = new HashMap<>();
					userData.put("result", authorization);
					userData.put("data", data.getUrlParams());
					application.publishEvent(new HimAuthorizationAfterEvent(himService,
							HimConst.AUTHORIZATION_AFTER_EVENT, "鉴定结果", userData));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
				return authorization;
			}
		});
		configuration.setPingTimeout(himProperties.getHeartbeatTimeout());
		configuration.setPingInterval(himProperties.getHeartbeatInterval());
		configuration.setOrigin(himProperties.getOrigin());
		SocketIOServer server = new SocketIOServer(configuration);
		log.info("正在启动Him服务器,服务端口为:{}", himProperties.getPort());
		// 绑定客户端上线事件
		server.addConnectListener(new ConnectListener() {
			@Override
			public void onConnect(SocketIOClient client) {

				HimUserInfo user = null;
				try {
					user = himService.connection(client);
					if (user != null) {
						user.setSessionId(client.getSessionId());
						client.set(HimConst.SESSION_USER, user);
						// 检测用户是否重复连接,如果重复连接,则强制关闭除当前以外的连接
						if (clients.containsKey(user.getId())) {
							clients.get(user.getId()).disconnect();
						}
						clients.put(user.getId(), client);
					}
					application
							.publishEvent(new HimEvent<HimUserInfo>(himService, HimConst.CONNECT_EVENT, "用户上线", user));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
			}
		});
		// 绑定客户端离线事件
		server.addDisconnectListener(new DisconnectListener() {
			@Override
			public void onDisconnect(SocketIOClient client) {
				HimUserInfo user = null;
				try {
					user = client.get(HimConst.SESSION_USER);
					if (user != null) {
						himService.disconnection(client, user);
					}
					application.publishEvent(
							new HimEvent<HimUserInfo>(himService, HimConst.DISCONNECT_EVENT, "用户离线", user));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
			}
		});
		// 好友消息
		server.addEventListener(HimConst.MESSAGE_FRIEND, Message.class, new DataListener<Message>() {
			@Override
			public void onData(SocketIOClient client, Message data, AckRequest ackSender) throws Exception {
				log.debug("接收消息[{}]:{}", HimConst.MESSAGE_FRIEND, data);
				try {
					himService.messageFriend(client, ackSender, data);
					application.publishEvent(new HimMessageEvent(himService, HimConst.MESSAGE_FRIEND, "好友消息", data));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
			}
		});
		// 群消息
		server.addEventListener(HimConst.MESSAGE_GROUP, Message.class, new DataListener<Message>() {
			@Override
			public void onData(SocketIOClient client, Message data, AckRequest ackSender) throws Exception {
				log.debug("接收消息[{}]:{}", HimConst.MESSAGE_GROUP, data);
				try {
					himService.messageGroup(client, ackSender, data);
					application.publishEvent(new HimMessageEvent(himService, HimConst.MESSAGE_GROUP, "群消息", data));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
			}
		});
		// 系统消息
		server.addEventListener(HimConst.MESSAGE_SYSTEM, Message.class, new DataListener<Message>() {
			@Override
			public void onData(SocketIOClient client, Message data, AckRequest ackSender) throws Exception {
				log.debug("接收消息[{}]:{}", HimConst.MESSAGE_SYSTEM, data);
				try {
					himService.messageSystem(client, ackSender, data);
					application.publishEvent(new HimMessageEvent(himService, HimConst.MESSAGE_SYSTEM, "系统消息", data));
				} catch (Exception e) {
					e.printStackTrace();
					application
							.publishEvent(new HimExceptionEvent(himService, HimConst.EXCEPTION_EVENT, e.getMessage()));
				}
			}
		});
		HimUtil.setServer(server);
		return server;
	}
}
